<?php
/**
 * @see Epic_Db_Adapter_Mysqli
 */
require_once 'Epic/Db/Adapter/Mysqli.php';

/**
 * @see Epic_Db_Model_Object
 */
require_once 'Epic/Db/Model/Object.php';

/**
 * @see Epic_Db_ObjectWatcher.php
 */
require_once 'Epic/Db/ObjectWatcher.php';

/**
 * @see Epic_Db_Select.php
 */
require_once 'Epic/Db/Select.php';

/**
 * @category   Epic
 * @package    Epic_Db
 * @author     Epic Core Team <nguyendnhat@gmail.com>
 */
abstract class Epic_Db_Data_Abstract
{
	/**
	 * Name table
	 * @var string
	 */
	protected $_nameTable;
	
	/**
	 * Key field primary name
	 * @var string
	 */
	protected $_idFieldName = 'id';
	
	/**
     * Adapter instance
     * @var Epic_Db_Adapter_Abstract
     */
    protected $_adapter;
    
    /**
     * Collection object
     * @var Epic_Db_Data_Collection
     */
    protected $_collection;
    
    /**
     * Query object
     * @var Epic_Db_Select
     */
    protected $_select;
        
    /**
     * Contructor
     */
    public function __construct()
    {		
        $this->_adapter = Epic::getAdapter();
    }
    
    /**
     * Retrieve adapter instance
     *
     * @return Epic_Db_Adapter_Interface
     */
    public function getAdapter()
    {
    	return $this->_adapter;
    }
    
    /**
     * Retrieve Query object
     *
     * @return Epic_Db_Select a query object
     */
    public function getSelect()
    {
    	if (is_null($this->_select)) {
    		$this->_select = new Epic_Db_Select();
    		$this->_select->from($this->_nameTable);
    	}
    	return $this->_select;
    }
    
    /**
     * Filter the data indicated by conditions
     *
     * @param string $field
     * @param array $condition
     * @return Epic_Db_Select
     */
    public function where($field, $condition)
    {
    	$this->getSelect()->where($field, $condition);
    	return $this;
    }
    
    /**
     * Indicate fileds received
     *
     * @param  string|array $fields
     * @return Epic_Db_Data_Abstract
     */
    public function field($fields)
    {
    	$this->getSelect()->field($fields);
    	return $this;
    }
    
    /**
     * Implement query count 
     *
     * @return int
     */
    public function count()
    {
    	$this->getAdapter()->query($this->getSelect()->count());
    	$row = $this->getAdapter()->fetchOne();
     	return $row['cnt'];
    }
	
    private function _getFromMap($id)
    {
    	return Epic_Db_ObjectWatcher::exists($this->_nameTable, $id);
    }
    
    private function _addToMap(Epic_Db_Model_Object $obj)
    {
    	return Epic_Db_ObjectWatcher::add($obj, $this->_nameTable);
    }
   
    /**
     * Retrive Collection object
     * 
     * @return Epic_Db_Data_Collection_Abstract
     */
    public function getCollection()
    {
    	// lazy load collection
    	if(is_null($this->_collection)) {
    		$this->_collection = new Epic_Db_Collection($this->getAdapter(), $this->_nameTable, $this->_idFieldName);
    	}
        return $this->_collection;
    }

    /**
     * Retrive a row by field, the default is the field id of the table 
     * 
     * @param  mixed value
     * @param  null|string $field column name of table
     * @throws Epic_Db_Exception if template không tồn tại
     * @return null|Epic_Db_Model_Object
     */
    public function load($value, $field = null)
    {
    	if(empty($value)) 
    		throw new Epic_Db_Exception('Value is not empty');

    	// Identity Map
    	$old = $this->_getFromMap($value);
    	if($old) {
    		return $old;
    	}
    
    	if ($field)
    		$where = $field . "= '{$value}'";
    	else
    		$where = $this->_idFieldName . '=' . $value;
    
    	$this->getAdapter()->select($this->_nameTable, $where);
        $row =  $this->getAdapter()->fetchOne();
        
        $obj = new Epic_Db_Model_Object($row);
        if($this->_idFieldName)
        	$obj->setIdFieldName($this->_idFieldName);
        // Identity Map
        $this->_addToMap($obj);
        
        return $obj;
    }
    
    /**
     * Update or Insert data to table 
     * 
     * @param array $data data update or insert to table
     */
	public function save(array $data = array())
	{
		if (array_key_exists($this->_idFieldName, $data)) {
			$this->getAdapter()->update($this->_nameTable, $data, "{$this->_idFieldName} = {$data[$this->_idFieldName]}");
		} else {
			$this->getAdapter()->insert($this->_nameTable, $data);
		}
	}
	 
	/**
	 * Delete a row by Id
	 * 
	 * @param int $id
	 */
	public function delete($id)
	{
	    $this->getAdapter()->delete($this->_nameTable, "{$this->_idFieldName} = $id");
	}
	
	/**
	 * Get the insertion ID
	 *
	 * @return null|int id insert
	 */
    public function getInsertId()
    {
    	return $this->getAdapter()->getInsertId();
    }
}